package sh.grapendaal.tsuushin.module.uno.engine.service.impl;

import java.security.SecureRandom;
import java.util.Collections;
import java.util.LinkedList;

import sh.grapendaal.tsuushin.module.uno.engine.domain.Card;
import sh.grapendaal.tsuushin.module.uno.engine.domain.Playable;
import sh.grapendaal.tsuushin.module.uno.engine.domain.Card.Rank;
import sh.grapendaal.tsuushin.module.uno.engine.domain.Card.Suit;
import sh.grapendaal.tsuushin.module.uno.engine.domain.Playable.Type;
import sh.grapendaal.tsuushin.module.uno.engine.service.CardService;
import sh.grapendaal.tsuushin.module.uno.engine.UnoException;

public class CardServiceImpl implements CardService {
	private static final int NUM_WILDCARDS = 4;
	private static final int NUM_CARDS_OF_EACH = 2;

	/**
	 * The deck contains cards that are not in possession of any other players.
	 * The cards have to be added in random order, because cards are popped of
	 * the head of this queue.
	 */
	private LinkedList<Card> deck = new LinkedList<Card>();

	/**
	 * The current open card, on which players will have to find a matching
	 * card.
	 */
	private Card topCard;

	@Override
	public void initialize() {
		this.deck = createDeck();
		// hopefully a better random
		Collections.shuffle(this.deck, new SecureRandom());
	}
	
	@Override
	public Card getCardFromDeck() {
		return this.deck.poll();
	}

	@Override
	public Card getTopCard() {
		return this.topCard;
	}
	
	protected LinkedList<Card> createDeck() {
		// Not using values() because not all values are used.
		Suit[] colors = { Suit.BLUE, Suit.GREEN, Suit.RED, Suit.YELLOW };
		Rank[] ranks = { Rank.ONE, Rank.TWO, Rank.THREE, Rank.FOUR, Rank.FIVE, Rank.SIX, Rank.SEVEN, Rank.EIGHT,
				Rank.NINE, Rank.DRAW_TWO, Rank.REVERSE, Rank.SKIP };

		LinkedList<Card> deck = new LinkedList<Card>();
		for (Suit suit : colors) {
			deck.add(new Card(suit, Rank.ZERO));

			for (Rank rank : ranks) {
				for (int i = 0; i < NUM_CARDS_OF_EACH; i++) {
					deck.add(new Card(suit, rank));
				}
			}
		}

		for (int i = 0; i < NUM_WILDCARDS; i++) {
			deck.add(new Card(Suit.WILDCARD, Rank.NONE));
			deck.add(new Card(Suit.WILDCARD, Rank.DRAW_FOUR));
		}
		
		return deck;
	}
	
	@Override
	public void openTopCard() {
		if (this.topCard != null) {
			throw new UnoException("Cannot open the top card more than once per game.");
		}
		
		// TODO Prevent special cards from being the first top card.
		this.topCard = getCardFromDeck();
	}
	
	@Override
	public void destroy() {
		this.topCard = null;
		this.deck = null;
	}

	@Override
	public void switchTopCard(Playable played) {
		if (this.topCard != null) {
			this.topCard.setNextSuit(null);
			this.deck.offer(this.topCard);
			this.topCard = null;
		}

		if (played.getType() == Type.CARD) {
			Card playedCard = (Card) played;
			this.topCard = playedCard;
		}
	}

}
